﻿using System.Collections.Immutable;

namespace DimensionsHelper.Serialization.CommandLine;

public class ArgumentObject
{

    /// <summary>
    /// 如果对象位于<see cref="ArgumentPropertyInfo.ListElementObject"/>，
    /// 将对应属性设置到此属性
    /// </summary>
    public ArgumentPropertyInfo? ContainingProperty { get; set; }

    /// <summary>
    /// 一般属性信息列表
    /// </summary>
    public ArgumentPropertyInfo[] Properties { get; set; } = Array.Empty<ArgumentPropertyInfo>();


    /// <summary>
    /// 字典属性信息列表
    /// </summary>
    public ArgumentDictionaryInfo[] Dictionaries { get; set; } = Array.Empty<ArgumentDictionaryInfo>();


    /// <summary>
    /// 目标对象的构造函数
    /// </summary>
    public Func<object>? ObjectCreator { get; set; }


    private static object? ResolvePropertyValue(ArgumentPropertyInfo propertyInfo, string value)
    {
        if (propertyInfo.Converter != null)
        {
            return propertyInfo.Converter(value);
        }

        return value;
    }


    private static void ResolveRegularProperty(object target, ArgumentPropertyInfo propertyInfo, string? value)
    {
        if (value != null)
        {
            propertyInfo.Setter?.Invoke(target, ResolvePropertyValue(propertyInfo, value));
        }
    }


    private static object GetOrSetPropertyValue(object target, ArgumentPropertyInfo propertyInfo)
    {
        object? value = null;

        if (propertyInfo.Getter != null)
        {
            value = propertyInfo.Getter(target);
        }

        if (value == null)
        {
            value = propertyInfo.ObjectCreator?.Invoke() ??
                throw new NotSupportedException();

            propertyInfo.Setter?.Invoke(target, value);
        }

        return value;
    }


    private class ValueGroup
    {

        private ValueGroup()
        {
            _groups = new List<Dictionary<string, string?>>();
        }


        private int _currentIndex = -1;
        private readonly List<Dictionary<string, string?>> _groups;


        public bool Next()
        {
            _currentIndex++;
            return _currentIndex < _groups.Count;
        }


        public string? GetValue(string argName)
        {
            if (_currentIndex >= _groups.Count)
            {
                return null;
            }

            return _groups[_currentIndex].TryGetValue(argName, out var val) ? val : null;
        }


        public static ValueGroup GroupValue(ArgumentPropertyInfo propertyInfo, ArgumentObject argumentObject, Func<string, string?> argValueGetter)
        {
            var group = new ValueGroup();
            group.GroupValues(propertyInfo, argumentObject, argValueGetter);
            return group;
        }


        private void GroupValues(ArgumentPropertyInfo propertyInfo, ArgumentObject argumentObject, Func<string, string?> argValueGetter)
        {
            if (_groups.Count > 0)
            {
                _groups.Clear();
            }

            //  arg1 = "a1,a2,a3,..."       { (arg1, a1), (arg2, b1), (arg3, c1),
            //  arg2 = "b1,b2,b3,..."  -->    (arg1, a2), (arg2, b2), (arg3, c2),
            //  arg3 = "c1,c2,c3,..."         (arg1, a3), (arg2, b3), (arg3, c3) }
            var argValues = new List<(ArgumentPropertyInfo Property, ImmutableArray<string> ArgValues)>();
            int maxSize = 0;

            // regular properties
            foreach (var p in argumentObject.Properties)
            {
                var argV = argValueGetter.Invoke(p.Name)?.Split(propertyInfo.ListSeparator).ToImmutableArray()
                    ?? ImmutableArray<string>.Empty;
                argValues.Add((p, argV));
                maxSize = Math.Max(maxSize, argV.Length);
            }

            // dictionary properties
            foreach (var d in argumentObject.Dictionaries)
            {
                var argK = argValueGetter.Invoke(d.Key.Name)?.Split(d.Key.ListSeparator).ToImmutableArray()
                    ?? ImmutableArray<string>.Empty;
                argValues.Add((d, argK));
                maxSize = Math.Max(maxSize, argK.Length);

                var argV = argValueGetter.Invoke(d.Value.Name)?.Split(d.Value.ListSeparator).ToImmutableArray()
                    ?? ImmutableArray<string>.Empty;
                argValues.Add((d, argV));
                maxSize = Math.Max(maxSize, argV.Length);
            }

            var lastEffectiveValues = new Dictionary<string, string>();

            for (int i = 0; i < maxSize; i++)
            {
                var elementValues = new Dictionary<string, string?>();
                // grouping
                foreach (var (Property, ValueArr) in argValues)
                {
                    string? current = null;

                    if (i < ValueArr.Length)
                    {
                        current = ValueArr[i];
                    }

                    // 如果是 null，检查是否允许沿用上一个有效值
                    if (string.IsNullOrEmpty(current) && 
                        Property.CanFollow &&
                        lastEffectiveValues.TryGetValue(Property.Name, out var lastValue))
                    {
                        current = lastValue;
                    }
                    // 更新有效值
                    else if (!string.IsNullOrEmpty(current))
                    {
                        if (!lastEffectiveValues.TryAdd(Property.Name, current))
                        {
                            lastEffectiveValues[Property.Name] = current;
                        }
                    }

                    elementValues.TryAdd(Property.Name, current);
                }

                _groups.Add(elementValues);
            }
        }


    }


    private static void ResolveListProperty(object target, ArgumentPropertyInfo propertyInfo, string? value, Func<string, string?> valueGetter)
    {
        object? list = GetOrSetPropertyValue(target, propertyInfo);

        // 单个值的列表
        if (propertyInfo.ListElementObject == null && value != null)
        {
            object? lastValue = null;

            foreach (var item in value.Split(propertyInfo.ListSeparator))
            {
                object? current = null;

                if (string.IsNullOrEmpty(item))
                {
                    if (propertyInfo.CanFollow && lastValue != null)
                    {
                        current = lastValue;
                    }
                }
                else
                {
                    current = ResolvePropertyValue(propertyInfo, item);
                    if (current != null)
                    {
                        lastValue = current;
                    }
                }

                propertyInfo.ListAddHandler?.Invoke(list, current);
            }
        }
        else if (propertyInfo.ListElementObject != null)
        {
            // 合并多个参数
            var valueGroup = ValueGroup.GroupValue(propertyInfo, propertyInfo.ListElementObject, valueGetter);

            while (valueGroup.Next())
            {
                propertyInfo.ListAddHandler?.Invoke(list, propertyInfo.ListElementObject.Resolve(valueGroup.GetValue));
            }
        }

    }


    private static void ResolveDictionaryProperty(object target, ArgumentDictionaryInfo dictionaryInfo, Func<string, string?> valueGetter)
    {
        object? targetDict = GetOrSetPropertyValue(target, dictionaryInfo);
        var keyVal = valueGetter.Invoke(dictionaryInfo.Key.Name);
        var valueVal = valueGetter.Invoke(dictionaryInfo.Value.Name);

        // 关键字不允许忽略
        if (string.IsNullOrEmpty(keyVal))
        {
            return;
        }

        // 逐个添加
        if (dictionaryInfo.DictionaryAddHandler != null)
        {
            var keyList = keyVal.Split(dictionaryInfo.Key.ListSeparator);
            var valList = valueVal == null ? Array.Empty<string>() 
                                           : valueVal.Split(dictionaryInfo.Value.ListSeparator);

            for (int i = 0; i < keyList.Length; i++)
            {
                string val = string.Empty;
                if (i < valList.Length)
                {
                    val = valList[i];
                }

                dictionaryInfo.DictionaryAddHandler(targetDict,
                    ResolvePropertyValue(dictionaryInfo.Key, keyList[i]),
                    ResolvePropertyValue(dictionaryInfo.Value, val));
            }
        }
    }


    public static void ResolveObjectProperty(object target, ArgumentPropertyInfo propertyInfo, Func<string, string?> valueGetter)
    {
        if (propertyInfo.PropertyObjectGetter != null)
        {
            var argObject = propertyInfo.PropertyObjectGetter.Invoke();
            if (argObject != null)
            {
                var obj = GetOrSetPropertyValue(target, propertyInfo);
                argObject.Resolve(obj, valueGetter);
            }
        }
    }


    public object Resolve(Func<string, string?> argValueGetter)
    {
        var obj = ObjectCreator?.Invoke() ?? throw new NotSupportedException();
        Resolve(obj, argValueGetter);
        return obj;
    }


    public void Resolve(object obj, Func<string, string?> argValueGetter)
    {
        // 非字典属性
        foreach (var prop in Properties)
        {
            switch (prop.Kind)
            {
                case ArgumentKind.List:
                    ResolveListProperty(obj, prop, argValueGetter(prop.Name), argValueGetter);
                    break;

                case ArgumentKind.Object:
                    ResolveObjectProperty(obj, prop, argValueGetter);
                    break;

                case ArgumentKind.Regular:
                default:
                    ResolveRegularProperty(obj, prop, argValueGetter(prop.Name));
                    break;
            }

        }

        // dict
        foreach (var dictProp in Dictionaries)
        {
            ResolveDictionaryProperty(obj, dictProp, argValueGetter);
        }
    }

}
